import abc
import time
from typing import Optional


# 关键点总结
# 装饰器函数：
# 接受一个函数 func 作为参数。
# 返回一个新的函数 wrapper。
# 嵌套函数（Wrapper Function）：
# 在 wrapper 函数中，可以在调用 func 之前和之后添加额外的逻辑。
# 使用 *args 和 **kwargs 来处理任意数量的位置参数和关键字参数。
# 使用 @ 符号：
# 简洁地将装饰器应用到目标函数上。
# 保留元数据：
# 使用 functools.wraps 装饰 wrapper 函数，以保留原函数的元数据（如名称、文档字符串等）。


#######################################################不嵌套的装饰器模式
def simple_decorator(func):
    print('装饰器正在运行~')
    return func


@simple_decorator
def action(name):
    time.sleep(1)
    print('你好~', name)


action('yinsc')

print('-' * 40)


#######################################################嵌套的装饰器模式
def count_time(func):
    def wrapper(name):
        t = time.time()
        print('装饰器开始运行：', t)
        func(name)
        print('耗时时间：', time.time() - t)

    return wrapper


@count_time
def action_wrapper(name):
    time.sleep(1)
    print('你好~~~', name)


action_wrapper('zhumq')

print('-' * 40)


####################################################### 装饰器上传参


def count_time_arg(msg=None):
    def count_time(func):
        def wrapper(name):
            t = time.time()
            print('装饰器开始运行：', t)
            func(name)
            print('耗时时间：', time.time() - t)
            print(f'最后传进来的msg为:{msg}')

        return wrapper

    return count_time


@count_time_arg('hello')
def action_arg(name):
    time.sleep(1)
    print('你好~~~', name)


action_arg('zhumq11111')

print('-' * 40)


####################################################### 使用类装饰器


class Unit:

    def count_time(self, func):
        def wrapper():
            t = time.time()
            print('开始计时:', t)
            func()
            print('结束计时:', t - time.time())

        return wrapper


unit = Unit()


@unit.count_time
def say():
    time.sleep(1)
    print('hello world')


say()

print('-' * 40)


####################################################### python常用内置装饰器

# @classmethod：@classmethod 装饰器用于定义类方法。类方法与普通方法不同，】
# 它在类层级上操作，而不是在实例层级上。通过类方法，我们可以直接通过类名调用方法，
# 而无需创建类的实例。并且第一个参数通常被命名为 cls，代表类本身，而不是常见的self。
# 类方法可以访问类的属性和其他类方法（不包括实例方法），但不能直接访问实例的属性。


class School:

    domain: str = "education"

    # get_domain表示类方法
    @classmethod
    def get_domain(cls):
        return cls.domain

# 输出education
print("通过类名访问: ", School.get_domain())
# 也可以通过实例去访问对象方法
school = School()
print("通过实例访问: ", school.get_domain())


# @property: @property装饰器是Python中一个特殊的装饰器，用于定义类的属性。
# 它提供了一种简洁的方式来定义属性的访问方法，并且可以在需要时进行计算或验证。
# 应用于类的实例方法，将其转换为类的属性。通过使用@property装饰器，
# 可以将一个方法定义为只读属性，也可以定义一个可读写的属性，并且可以实现属性删除。

# @setter: @setter装饰器用于定义一个属性的setter方法，用于修改属性的值。
# 使用@setter时，我们需要在@property装饰的属性方法之后，
# 紧跟一 个setter方法，并使用@属性名.setter来装饰该方法

# @setter通常用于以下场景：
# 1.当一个属性的值需要计算得到，而不是直接存储在类的属性中时，
# 我们可以使用@setter来提供一个修改属性值的接口。
# 2.当某个属性的值需要经过一些处理后再进行存储时，
# 我们可以使用@setter来自动执行处理操作。

# 注意：
# 静态方法和类方法：
# @property装饰器只能应用于实例方法，而不能应用于静态方法或类方法。它是用于访问实例属性的装饰器。
#
# 私有属性：
# 私有属性是名称前面添加一个下划线，例如self._property。这样可以向其他开发人员传达属性的可见性意图，虽然在Python中并没有真正的私有属性，私有属性也可以被访问。
#
# 调用setter：
# 在setter方法内部，我们使用self.属性名对属性进行赋值。在调用setter方法时，我们只需要给属性赋值，不需要加圆括号。

print('-' * 40)

class Person:

    def __init__(self, name):
        # 定义私有属性_name
        self._name = name

    # 支持外部访问name私有属性
    # print(person.name)：外部无法直接访问私有属性，加上这个方法外部就可以访问私有属性
    @property
    def name(self):
        print('返回name属性', self._name)
        return self._name

    # 支持外部设置name私有属性
    # person.name = '殷世聪': 外部无法直接设置私有属性，当前方法可以支持在外部设置私有属性
    @name.setter
    def name(self,name):
        print('设置name属性', name)
        self._name = name

    # 支持外部删除name私有属性
    # del person.name: 外部无法直接删除私有属性，当前方法可以支持在外部删除私有属性
    @name.deleter
    def name(self):
        print('删除name属性', self._name)
        del self._name

person = Person("yinsc")
print(person.name)
person.name = '殷世聪'
print(person.name)
del person.name


# @staticmethod装饰器基本用法
# 定义
# @staticmethod装饰器是Python中用于定义静态方法的装饰器。
# 静态方法是与类相关联但不依赖于实例的方法。它们可以直接通过类名调用，而无需创建类的实例。
#
# 静态方法具有以下特点：
#
# 不访问实例状态：静态方法不需要访问实例的状态或属性。它们通常用于执行与类相关的功能，但与特定实例无关。
#
# 通过类名调用：静态方法可以直接通过类名调用，而无需创建类的实例。这使得静态方法在不依赖于实例的情况下提供了一种方便的功能。
#
# 不需要self参数：静态方法的定义不需要self参数。由于静态方法不操作实例的状态，因此不需要将实例作为参数传递。
#
# 不能访问实例变量：静态方法无法访问实例变量，因为它们与实例无关。它们只能访问类级别的属性和其他静态方法。
#
# 可以访问类级别的属性和方法：静态方法可以访问类级别的属性和其他静态方法。它们可以与类的其他成员进行交互，但无法直接访问实例级别的成员。


# @abstractmethod装饰器基本用法
# @abstractmethod 是一个装饰器，用于声明抽象方法。
# 抽象方法是在基类中定义但没有具体实现的方法，它需要在派生类中进行具体的实现。

print('-' * 40)
from abc import ABC, abstractmethod  # 第一步：导入 abc

# 定义抽象基类
class Animal(ABC):  # 同一类事物:动物
    @abstractmethod
    def talk(self):
        pass

class Dog(Animal):
    def talk(self):
        print('this is a dog~')

class Cat(Animal):
    def talk(self):
        print('this is a cat~')

cat = Cat()
cat.talk()

dog = Dog()
dog.talk()